
function XF_Debug()
{
  this.On = false;
  this.Stack = new Array();
  this.DebugWindow = null;
  this.DebugWindowTitle = 'Debug Window';
  this.ExecProfile = new Object();
}

XF_Debug.prototype.Push = function ()
{
  this.Stack[this.Stack.length] = this.On;
  this.On = true;
}

XF_Debug.prototype.Pop = function ()
{
  this.On = this.Stack[this.Stack.length - 1];
  --this.Stack.length;
}

XF_Debug.prototype.Open =  function ()
{
  if (this.DebugWindow && !this.DebugWindow.closed)
    this.Close();
    
  this.DebugWindow = window.open('about:blank', 'XF_DEBUGWINDOW', 'height=400,width=600,resizable=yes,scrollbars=yes');

  this.DebugWindow.title = this.DebugWindowTitle;
  this.DebugWindow.document.write('<html><head><title>' + this.DebugWindowTitle + '</title></head><body><h3>Javascript Debug Window</h3></body></html>');
  this.DebugWindow.focus();
}

XF_Debug.prototype.Close = function ()
{
  if (!this.DebugWindow)
    return;
    
  if (!this.DebugWindow.closed)
    this.DebugWindow.close();
}

XF_Debug.prototype.Write = function (msg)
{
	var re;
	var result;

	if (!this.On)
	    return;
    
	if (!this.DebugWindow || this.DebugWindow.closed)
		this.Open();
	
	re = /</g;
	result = msg.replace(re, "&lt;");

	re = />/g;
	result = result.replace(re, "&gt;");
	
	this.DebugWindow.document.write(result + '<br>');
  
	return;
}

function XF_DebugGetFunctionName(funcRef)
{

  if (funcRef.name)
    return funcRef.name;

  var name = funcRef + '';
  name = name.substring(name.indexOf(' ') + 1, name.indexOf('('));
  funcRef.name = name;

  return name;
}

function XF_DebugCreateFunctionWrapper(scopeName, funcName, preCall, postCall)
{
  var wrappedFunc;
  var scopeObject = eval(scopeName);
  var funcRef = scopeObject[funcName];

  scopeObject['XF_Debug_orig_' + funcName] = funcRef;

  wrappedFunc = function () 
  {
    preCall(scopeName, funcName, arguments);
    var rv = funcRef.apply(this, arguments);
    postCall(scopeName, funcName, arguments, rv);
    return rv;
  };

  if (typeof(funcRef.constructor) != 'undefined')
    wrappedFunc.constructor = funcRef.constuctor;

  if (typeof(funcRef.prototype) != 'undefined')
    wrappedFunc.prototype = funcRef.prototype;

  scopeObject[funcName] = wrappedFunc;
}

XF_Debug.prototype.TraceFunction = function (scopeName, funcName)
{
  XF_DebugCreateFunctionWrapper(scopeName, funcName, XF_DebugTraceBefore, XF_DebugTraceAfter);
}

function XF_DebugPersistToString(obj)
{
  var s = '';
  var p;

  if (obj == null)
     return 'null';

  switch(typeof(obj))
  {
    case 'number':
       return obj;
    case 'string':
       return '"' + obj + '"';
    case 'undefined':
       return 'undefined';
    case 'boolean':
       return obj + '';
    case 'object':
	   return obj.id;
  }

  return '[' + XF_DebugGetFunctionName(obj.constructor) + ']';
}

function XF_DebugTraceBefore(scopeName, funcName, funcArguments) 
{
  var i;
  var s = '';
  var execProfile = xf_DEBUG.ExecProfile[scopeName + '.' + funcName];
  if (!execProfile)
    execProfile = xf_DEBUG.ExecProfile[scopeName + '.' + funcName] = { Started: 0, Time: 0, Count: 0 };

  for (i = 0; i < funcArguments.length; i++)
  {
    s += XF_DebugPersistToString(funcArguments[i]);
    if (i < funcArguments.length - 1)
      s += ', ';
  }

  xf_DEBUG.Write('enter ' + scopeName + '.' + funcName + '(' + s + ')');
  execProfile.Started = (new Date()).getTime();
}

function XF_DebugTraceAfter(scopeName, funcName, funcArguments, rv) 
{
  var i;
  var s = '';
  var execProfile = xf_DEBUG.ExecProfile[scopeName + '.' + funcName];
  if (!execProfile)
    xf_DEBUG.Write('XF_DebugTraceAfter: ExecProfile not created for ' + scopeName + '.' + funcName);
  else if (execProfile.Started == 0)
    xf_DEBUG.Write('XF_DebugTraceAfter: ExecProfile.Started == 0 for ' + scopeName + '.' + funcName);
  else 
  {
    execProfile.Time += (new Date()).getTime() - execProfile.Started;
    execProfile.Count++;
    execProfile.Started = 0;
  }

  for (i = 0; i < funcArguments.length; i++)
  {
    s += XF_DebugPersistToString(funcArguments[i]);
    if (i < funcArguments.length - 1)
      s += ', ';
  }

  xf_DEBUG.Write('exit  ' + scopeName + '.' + funcName + '(' + s + ')==' + XF_DebugPersistToString(rv));
}

XF_Debug.prototype.TraceObject = function (scopeName, objName)
{
  var objRef = eval(scopeName + '.' + objName);
  var p;

  if (!objRef || !objRef.prototype)
     return;

  for (p in objRef.prototype)
  {
    if (typeof(objRef.prototype[p]) == 'function' && (p+'').indexOf('XF_Debug_orig') == -1)
    {
      XF_DebugCreateFunctionWrapper(scopeName + '.' + objName + '.prototype', p + '', XF_DebugTraceBefore, XF_DebugTraceAfter);
    }
  }
}

XF_Debug.prototype.WriteProfile = function ()
{
	var p;
	var execProfile;
	var avg;

	for (p in xf_DEBUG.ExecProfile)
	{
		execProfile = xf_DEBUG.ExecProfile[p];
		avg = Math.round ( 100 * execProfile.Time/execProfile.Count) /100;
		xf_DEBUG.Write('Execution profile ' + p + ' called ' + execProfile.Count + ' times. Total time=' + execProfile.Time + 'ms. Avg Time=' + avg + 'ms.');
	}
}

XF_Debug.prototype.WriteObjectInfo = function (objName, objRef)
{
	var name;
	var type;
	var value;
	
	type = typeof(objRef);
	switch(type)
	{
		case 'number':
			xf_DEBUG.Write(objName + '(' + type + ') : ' + value);
			break;
		case 'string':
			xf_DEBUG.Write(objName + '(' + type + ') : ' + value);
			break;
		case 'undefined':
			xf_DEBUG.Write(objName + '(' + type + ') : undefined');
			break;
		case 'boolean':
			xf_DEBUG.Write(objName + '(' + type + ') : ' + value);
			break;
		case 'object':
			xf_DEBUG.Write(objName + '(' + type + ') : ');
			for (name in objRef)
			{
				type = typeof(objRef[name]);
				value = '';
				switch(type)
				{
					case 'number':
						value = objRef[name];
						break;
					case 'string':
						value = objRef[name];
						break;
					case 'undefined':
						value = 'undefined';
						break;
					case 'boolean':
						value = objRef[name];
						break;
				}
				xf_DEBUG.Write('&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' + name + '(' + type + ') : ' + value);
			}	
			break;
	}
}

var xf_DEBUG = new XF_Debug();

window.onunload = function () { xf_DEBUG.Close(); }

/*
			function World()
			{
				this.isTurning = false;
			}
			World.prototype.turn = function (on) { this.isTurning = on; return on;}

			// define a function to be traced
			function Hello(s)
			{
				var world = new World();
				world.turn(true);
				alert('Hello ' + s);
			}

			xf_DEBUG.Push();
			if (xf_DEBUG.On)
			{
				// set traces
				xf_DEBUG.TraceObject('window', 'World');
				xf_DEBUG.TraceFunction('window', 'Hello');
			}
			// turn off debugging
			xf_DEBUG.Pop();

			xf_DEBUG.Write('This will not be displayed');

			function init()
			{
				// turn on debugging
				xf_DEBUG.Push();
				// this will be traced
				Hello('World!');
				// turn off debugging
				xf_DEBUG.Pop();
			}

*/